package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.utils.JacksonUtils;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.dto.WinningRecordResultDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final String WINNING_RECORD_PREFIX = "WINNING_RECORD_";

    private final Long WINNING_RECORD_TIMEOUT = 60*60*24*3L;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Override
    public void drawPrize(DrawPrizeParam param) {

        //消息体
        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtils.writeValueString(param));
        //发信息：交换机 绑定的key 和信息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        logger.info("mq信息发送成功 map{}"+JacksonUtils.writeValueString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {

        //活动\奖品 是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //获取奖品参数可以选择activity_prize 因为等下需要用到奖品数量
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        if(null == activityDO || null == activityPrizeDO) {

            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            logger.info("校验抽取请求失败！失败原因{}",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);

            return false;
        }
        //活动状态是否有效
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            logger.info("校验抽取请求失败！失败原因{}",ServiceErrorCodeConstants.ACTIVITY_COMPLETED);

            return false;
        }

        //奖品状态是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            //throw new ServiceException(ServiceErrorCodeConstants.PRIZE_COMPLETED);
            logger.info("校验抽取请求失败！失败原因{}",ServiceErrorCodeConstants.PRIZE_COMPLETED);

            return false;
        }
        //奖品数量 等于 中奖人数
        if(activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
            //throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            logger.info("校验抽取请求失败！失败原因{}",ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinningRecords(DrawPrizeParam param) {
        //查询相关信息：活动 活动关联的奖品 人员 奖品
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        List<Long> winnerIds = param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.winner::getUserId)
                .collect(Collectors.toList());
        List<UserDO> userDOList = userMapper.selectListByIds(winnerIds);
        //保持中奖者信息到数据库
        List<WinningRecordDO> winningRecordDOList = userDOList
                .stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOList);
        //缓存中奖者记录
        //1.缓存奖品维度中奖信息(activity_prize,winningRecordDOList)
        cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId()
                ,winningRecordDOList
                ,WINNING_RECORD_TIMEOUT);
        //2.缓存活动维度中奖信息(activity,winningRecordDOList)
        //当活动已完成 才能进行去缓存
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            List<WinningRecordDO> allList = winningRecordMapper.selectByactivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),allList,WINNING_RECORD_TIMEOUT);
        }
        return winningRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        //删除数据表
        if(null == activityId) {
            logger.info("要删除中奖记录关联的活动Id为空！");
            return;
        }
        winningRecordMapper.deleteRecords(activityId,prizeId);

        if(prizeId != null) {
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //删除缓存 活动维度 奖品维度
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordResultDTO> getRecords(ShowWinningRecordsParam param) {
        // 查询redis: 奖品、活动
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWinningRecordResultDTO(winningRecordDOList);
        }
        // 如果redis不存在，查库
        winningRecordDOList = winningRecordMapper.selectByactivityIdOrPrizeId(
                param.getActivityId(), param.getPrizeId());
        // 存放记录到redis
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("查询的中奖记录为空！param:{}",
                    JacksonUtils.writeValueString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key, winningRecordDOList, WINNING_RECORD_TIMEOUT);
        return convertToWinningRecordResultDTO(winningRecordDOList);
    }

    private List<WinningRecordResultDTO> convertToWinningRecordResultDTO(
            List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            return Arrays.asList();
        }
        return winningRecordDOList
                .stream()
                .map(winningRecordDO -> {
                    WinningRecordResultDTO winningRecordResultDTO = new WinningRecordResultDTO();
                    winningRecordResultDTO.setWinningId(winningRecordDO.getWinnerId());
                    winningRecordResultDTO.setWinnerName(winningRecordDO.getWinnerName());
                    winningRecordResultDTO.setPrizeName(winningRecordDO.getPrizeName());
                    winningRecordResultDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    winningRecordResultDTO.setWinningTime(winningRecordDO.getWinningTime());
                    return winningRecordResultDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 从缓存中删除数据
     * @param key
     */
    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECORD_PREFIX+key)) {
                // 存在缓存 删除数据
                redisUtil.del(WINNING_RECORD_PREFIX+key);
            }
        }catch (Exception e) {
            logger.error("从缓存中删除失败，key{}"+key);
        }
    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winningRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key, List<WinningRecordDO> winningRecordDOList, Long time) {
        String str = JacksonUtils.writeValueString(winningRecordDOList);
        try {
            if(!StringUtils.hasText(key) ||
                    CollectionUtils.isEmpty(winningRecordDOList)) {
                logger.warn("要缓存的内容为空！key{},value{}",key,str);
                return;
            }
            redisUtil.set(WINNING_RECORD_PREFIX+ key,
                    str,
                    time);

        }catch (Exception e) {
            logger.error("缓存中奖记录异常！key{},value{}",WINNING_RECORD_PREFIX+key,str);
        }
    }

    /**
     * 从缓存redis中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                logger.warn("要从缓存中查询中奖记录的key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORD_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }

            return JacksonUtils.readListValue(str, WinningRecordDO.class);
        } catch (Exception e) {
            logger.error("从缓存中查询中奖记录异常！key:{}", WINNING_RECORD_PREFIX + key);
            return Arrays.asList();
        }
    }

}
